using System;
using System.Diagnostics;
using Dida.Dbg;
using Tao.OpenGl;

namespace Dida
{
	internal class OpenGLDebugDrawImpl : DebugDrawImpl
	{
		/// <summary>
		/// The drawing mode we are currently in, ie. the type of glBegin()/glEnd block
		/// where are in. This variable should have the same value as the
		/// constant you pass glBegin, or -1 if no drawing mode is set.
		/// </summary>
		int drawingMode;
		Viewpoint view;
		Matrix4 mat;

		public override void Begin(Viewpoint view)
		{
			drawingMode = -1;
			this.view = view;
			
			Gl.glMatrixMode(Gl.GL_PROJECTION);
			Gl.glLoadIdentity();
			Gl.glLoadMatrixf(view.ProjectionMatrix.ToTransposedFloatArray());

			Vector3D lightVec = -view.ViewMatrix.GetBaseVector(2);
			lightVec.y += .5f;
			lightVec.Normalize();

			Gl.glEnable(Gl.GL_LIGHT0);
			Gl.glEnable(Gl.GL_COLOR_MATERIAL);
			float[] vec4d = new float[]
				{ lightVec.x,lightVec.y,lightVec.z,0, };
			Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, vec4d);
						
			Debug.Assert(Gl.glGetError() == Gl.GL_NO_ERROR, "An OpenGl error occured");
		}

		public override void End()
		{
			if(drawingMode != -1)
				Gl.glEnd();

			Gl.glEnable(Gl.GL_LIGHTING);
		}

		public override void SetWorldMatrix(Matrix4 mat)
		{
			this.mat = mat;

			if (drawingMode != -1)
			{
				Gl.glEnd();
				drawingMode = -1;
			}

			Matrix4 modelView = mat * view.ViewMatrix;

			Gl.glMatrixMode(Gl.GL_MODELVIEW);
			Gl.glLoadIdentity();
			Gl.glLoadMatrixf(modelView.ToTransposedFloatArray());
		}

		public override void DrawLine(Vector3D start, Vector3D end, uint colour)
		{
			if (drawingMode != Gl.GL_LINES)
			{
				if (drawingMode != -1)
					Gl.glEnd();

				Gl.glDisable(Gl.GL_LIGHTING);

				Gl.glBegin(Gl.GL_LINES);
				drawingMode = Gl.GL_LINES;
			}

			Gl.glColor3f(((colour >> 16)& 0xff) / (float)0xff,
				((colour >> 8) & 0xff) / (float)0xff,
				(colour & 0xff) / (float)0xff);
			Gl.glVertex3f(start.x, start.y, start.z);
			Gl.glVertex3f(end.x, end.y, end.z);
		}

		public override void DrawTriangle(Vector3D[] verts, uint colour)
		{
			if (drawingMode != Gl.GL_TRIANGLES)
			{
				if (drawingMode != -1)
					Gl.glEnd();

				Gl.glDisable(Gl.GL_LIGHTING);

				Gl.glBegin(Gl.GL_TRIANGLES);
				drawingMode = Gl.GL_TRIANGLES;
			}

			Gl.glColor3f((colour & 0xff) / (float)0xff,
				((colour >> 8) & 0xff) / (float)0xff,
				((colour >> 16) & 0xff) / (float)0xff);
			Gl.glVertex3f(verts[0].x, verts[0].y, verts[0].z);
			Gl.glVertex3f(verts[1].x, verts[1].y, verts[1].z);
			Gl.glVertex3f(verts[2].x, verts[2].y, verts[2].z);
		}

		public override IDebugMeshImpl CreateDebugMesh()
		{
			return new OpenGLDebugMeshImpl();
		}

		public override void DrawDebugMesh(DebugMesh mesh)
		{
			// the debug mesh drawing calls it's own glBegin/glEnd, so we
			// have to make sure that we won't be in a glBegin/glEnd block already.
			if (drawingMode != -1)
			{
				Gl.glEnd();
				drawingMode = -1;

				Gl.glEnable(Gl.GL_LIGHTING);
			}

			((OpenGLDebugMeshImpl)mesh.Implementation).Draw(view,mat);
		}
	}
}
